/* BEGIN_HEADER */
#include <stdint.h>

#include "psa_crypto_slot_management.h"
#include "psa_crypto_storage.h"

typedef enum {
    /**< Close key(s) */
    INVALIDATE_BY_CLOSING,

    /**< Destroy key(s) */
    INVALIDATE_BY_DESTROYING,

    /**< Purge key(s) */
    INVALIDATE_BY_PURGING,

    /**< Terminate and reinitialize without closing/destroying keys */
    INVALIDATE_BY_SHUTDOWN,

    /**< Close key(s) then terminate and re-initialize */
    INVALIDATE_BY_CLOSING_WITH_SHUTDOWN,

    /**< Destroy key(s) then terminate and re-initialize */
    INVALIDATE_BY_DESTROYING_WITH_SHUTDOWN,

    /**< Purge key(s) then terminate and re-initialize */
    INVALIDATE_BY_PURGING_WITH_SHUTDOWN,
} invalidate_method_t;

typedef enum {
    KEEP_OPEN,
    CLOSE_BEFORE,
    CLOSE_AFTER,
} reopen_policy_t;

typedef enum {
    INVALID_HANDLE_0,
    INVALID_HANDLE_UNOPENED,
    INVALID_HANDLE_CLOSED,
    INVALID_HANDLE_HUGE,
} invalid_handle_construction_t;

/** Apply \p invalidate_method to invalidate the specified key:
 * close it, destroy it, or do nothing;
 */
static int invalidate_key(invalidate_method_t invalidate_method,
                          mbedtls_svc_key_id_t key)
{
    switch (invalidate_method) {
        /* Closing the key invalidate only volatile keys, not persistent ones. */
        case INVALIDATE_BY_CLOSING:
        case INVALIDATE_BY_CLOSING_WITH_SHUTDOWN:
            PSA_ASSERT(psa_close_key(key));
            break;
        case INVALIDATE_BY_DESTROYING:
        case INVALIDATE_BY_DESTROYING_WITH_SHUTDOWN:
            PSA_ASSERT(psa_destroy_key(key));
            break;
        /* Purging the key just purges RAM data of persistent keys. */
        case INVALIDATE_BY_PURGING:
        case INVALIDATE_BY_PURGING_WITH_SHUTDOWN:
            PSA_ASSERT(psa_purge_key(key));
            break;
        case INVALIDATE_BY_SHUTDOWN:
            break;
    }
    return 1;
exit:
    return 0;
}

/** Restart the PSA subsystem if \p invalidate_method says so. */
static int invalidate_psa(invalidate_method_t invalidate_method)
{
    switch (invalidate_method) {
        case INVALIDATE_BY_CLOSING:
        case INVALIDATE_BY_DESTROYING:
        case INVALIDATE_BY_PURGING:
            return 1;
        case INVALIDATE_BY_CLOSING_WITH_SHUTDOWN:
        case INVALIDATE_BY_DESTROYING_WITH_SHUTDOWN:
        case INVALIDATE_BY_PURGING_WITH_SHUTDOWN:
            /* All keys must have been closed. */
            PSA_SESSION_DONE();
            break;
        case INVALIDATE_BY_SHUTDOWN:
            /* Some keys may remain behind, and we're testing that this
             * properly closes them. */
            mbedtls_psa_crypto_free();
            break;
    }

    PSA_ASSERT(psa_crypto_init());
    ASSERT_PSA_PRISTINE();
    return 1;

exit:
    return 0;
}

/* END_HEADER */

/* BEGIN_DEPENDENCIES
 * depends_on:MBEDTLS_PSA_CRYPTO_C
 * END_DEPENDENCIES
 */

/* BEGIN_CASE */
void transient_slot_lifecycle(int owner_id_arg,
                              int usage_arg, int alg_arg,
                              int type_arg, data_t *key_data,
                              int invalidate_method_arg)
{
    psa_algorithm_t alg = alg_arg;
    psa_key_usage_t usage_flags = usage_arg;
    psa_key_type_t type = type_arg;
    invalidate_method_t invalidate_method = invalidate_method_arg;
    mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;

    mbedtls_test_set_step(1);
    PSA_ASSERT(psa_crypto_init());

    /* Import a key. */
#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
    mbedtls_key_owner_id_t owner_id = owner_id_arg;

    mbedtls_set_key_owner_id(&attributes, owner_id);
#else
    (void) owner_id_arg;
#endif

    psa_set_key_usage_flags(&attributes, usage_flags);
    psa_set_key_algorithm(&attributes, alg);
    psa_set_key_type(&attributes, type);
    PSA_ASSERT(psa_import_key(&attributes, key_data->x, key_data->len,
                              &key));
    TEST_ASSERT(!mbedtls_svc_key_id_is_null(key));
    PSA_ASSERT(psa_get_key_attributes(key, &attributes));
    TEST_EQUAL(psa_get_key_type(&attributes), type);
    psa_reset_key_attributes(&attributes);

#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
    {
        psa_key_handle_t handle;
        mbedtls_svc_key_id_t key_with_invalid_owner =
            mbedtls_svc_key_id_make(owner_id + 1,
                                    MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key));

        TEST_ASSERT(mbedtls_key_owner_id_equal(
                        owner_id,
                        MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(key)));
        TEST_EQUAL(psa_open_key(key_with_invalid_owner, &handle),
                   PSA_ERROR_DOES_NOT_EXIST);
    }
#endif

    /*
     * Purge the key and make sure that it is still valid, as purging a
     * volatile key shouldn't invalidate/destroy it.
     */
    PSA_ASSERT(psa_purge_key(key));
    PSA_ASSERT(psa_get_key_attributes(key, &attributes));
    TEST_EQUAL(psa_get_key_type(&attributes), type);
    psa_reset_key_attributes(&attributes);

    /* Do something that invalidates the key. */
    mbedtls_test_set_step(2);
    if (!invalidate_key(invalidate_method, key)) {
        goto exit;
    }
    if (!invalidate_psa(invalidate_method)) {
        goto exit;
    }

    /* Test that the key is now invalid. */
    TEST_EQUAL(psa_get_key_attributes(key, &attributes),
               PSA_ERROR_INVALID_HANDLE);
    TEST_EQUAL(psa_close_key(key), PSA_ERROR_INVALID_HANDLE);

exit:
    /*
     * Key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&attributes);

    PSA_DONE();
}
/* END_CASE */

/* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_STORAGE_C */
void persistent_slot_lifecycle(int lifetime_arg, int owner_id_arg, int id_arg,
                               int usage_arg, int alg_arg, int alg2_arg,
                               int type_arg, data_t *key_data,
                               int invalidate_method_arg)
{
    psa_key_lifetime_t lifetime = lifetime_arg;
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(owner_id_arg, id_arg);
    psa_algorithm_t alg = alg_arg;
    psa_algorithm_t alg2 = alg2_arg;
    psa_key_usage_t usage_flags = usage_arg;
    psa_key_type_t type = type_arg;
    invalidate_method_t invalidate_method = invalidate_method_arg;
    mbedtls_svc_key_id_t returned_id = MBEDTLS_SVC_KEY_ID_INIT;
    psa_key_handle_t handle = PSA_KEY_HANDLE_INIT;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    psa_key_attributes_t read_attributes = PSA_KEY_ATTRIBUTES_INIT;
    uint8_t *reexported = NULL;
    size_t reexported_length = -1;

#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
    mbedtls_svc_key_id_t wrong_owner_id =
        mbedtls_svc_key_id_make(owner_id_arg + 1, id_arg);
    mbedtls_svc_key_id_t invalid_svc_key_id = MBEDTLS_SVC_KEY_ID_INIT;
#endif

    TEST_USES_KEY_ID(id);

    mbedtls_test_set_step(1);
    PSA_ASSERT(psa_crypto_init());

    psa_set_key_id(&attributes, id);
    psa_set_key_lifetime(&attributes, lifetime);
    psa_set_key_type(&attributes, type);
    psa_set_key_usage_flags(&attributes, usage_flags);
    psa_set_key_algorithm(&attributes, alg);
    psa_set_key_enrollment_algorithm(&attributes, alg2);
    PSA_ASSERT(psa_import_key(&attributes, key_data->x, key_data->len,
                              &returned_id));
    TEST_ASSERT(mbedtls_svc_key_id_equal(id, returned_id));

#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
    TEST_EQUAL(psa_open_key(wrong_owner_id, &invalid_svc_key_id),
               PSA_ERROR_DOES_NOT_EXIST);
#endif

    PSA_ASSERT(psa_get_key_attributes(id, &attributes));
    TEST_EQUAL(psa_get_key_lifetime(&attributes), lifetime);
    TEST_ASSERT(mbedtls_svc_key_id_equal(
                    psa_get_key_id(&attributes), id));
    TEST_EQUAL(psa_get_key_usage_flags(&attributes),
               mbedtls_test_update_key_usage_flags(usage_flags));
    TEST_EQUAL(psa_get_key_algorithm(&attributes), alg);
    TEST_EQUAL(psa_get_key_enrollment_algorithm(&attributes), alg2);
    TEST_EQUAL(psa_get_key_type(&attributes), type);

    /* Close the key and then open it. */
    PSA_ASSERT(psa_close_key(id));

#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
    TEST_EQUAL(psa_open_key(wrong_owner_id, &invalid_svc_key_id),
               PSA_ERROR_DOES_NOT_EXIST);
#endif

    PSA_ASSERT(psa_open_key(id, &handle));
    TEST_ASSERT(!psa_key_handle_is_null(handle));
    PSA_ASSERT(psa_get_key_attributes(handle, &attributes));
    TEST_EQUAL(psa_get_key_lifetime(&attributes), lifetime);
    TEST_ASSERT(mbedtls_svc_key_id_equal(
                    psa_get_key_id(&attributes), id));
    TEST_EQUAL(psa_get_key_usage_flags(&attributes),
               mbedtls_test_update_key_usage_flags(usage_flags));
    TEST_EQUAL(psa_get_key_algorithm(&attributes), alg);
    TEST_EQUAL(psa_get_key_enrollment_algorithm(&attributes), alg2);
    TEST_EQUAL(psa_get_key_type(&attributes), type);

    /*
     * Do something that wipes key data in volatile memory or destroy the
     * key.
     */
    mbedtls_test_set_step(2);
    if (!invalidate_key(invalidate_method, id)) {
        goto exit;
    }
    if (!invalidate_psa(invalidate_method)) {
        goto exit;
    }

    /* Try to reaccess the key. If we destroyed it, check that it doesn't
     * exist. Otherwise check that it still exists and has the expected
     * content. */
    switch (invalidate_method) {
        case INVALIDATE_BY_CLOSING:
        case INVALIDATE_BY_CLOSING_WITH_SHUTDOWN:
        case INVALIDATE_BY_PURGING:
        case INVALIDATE_BY_PURGING_WITH_SHUTDOWN:
        case INVALIDATE_BY_SHUTDOWN:
            PSA_ASSERT(psa_open_key(id, &handle));
            PSA_ASSERT(psa_get_key_attributes(id, &read_attributes));
            TEST_EQUAL(psa_get_key_lifetime(&attributes),
                       psa_get_key_lifetime(&read_attributes));
            TEST_ASSERT(mbedtls_svc_key_id_equal(
                            psa_get_key_id(&attributes),
                            psa_get_key_id(&read_attributes)));
            TEST_EQUAL(psa_get_key_usage_flags(&attributes),
                       mbedtls_test_update_key_usage_flags(usage_flags));
            TEST_EQUAL(psa_get_key_algorithm(&attributes),
                       psa_get_key_algorithm(&read_attributes));
            TEST_EQUAL(psa_get_key_enrollment_algorithm(&attributes),
                       psa_get_key_enrollment_algorithm(&read_attributes));
            TEST_EQUAL(psa_get_key_type(&attributes),
                       psa_get_key_type(&read_attributes));
            TEST_EQUAL(psa_get_key_bits(&attributes),
                       psa_get_key_bits(&read_attributes));
            ASSERT_ALLOC(reexported, key_data->len);
            if (usage_flags & PSA_KEY_USAGE_EXPORT) {
                PSA_ASSERT(psa_export_key(id, reexported, key_data->len,
                                          &reexported_length));
                ASSERT_COMPARE(key_data->x, key_data->len,
                               reexported, reexported_length);
            } else {
                TEST_EQUAL(psa_export_key(id, reexported,
                                          key_data->len, &reexported_length),
                           PSA_ERROR_NOT_PERMITTED);
            }
            PSA_ASSERT(psa_close_key(handle));
            break;

        case INVALIDATE_BY_DESTROYING:
        case INVALIDATE_BY_DESTROYING_WITH_SHUTDOWN:
            /*
             * Test that the key handle and identifier are now not referring to an
             * existing key.
             */
            TEST_EQUAL(psa_get_key_attributes(handle, &read_attributes),
                       PSA_ERROR_INVALID_HANDLE);
            TEST_EQUAL(psa_close_key(handle), PSA_ERROR_INVALID_HANDLE);
            TEST_EQUAL(psa_get_key_attributes(id, &read_attributes),
                       PSA_ERROR_INVALID_HANDLE);
            break;
    }

exit:
    /*
     * Key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&attributes);
    psa_reset_key_attributes(&read_attributes);

    PSA_DONE();
    mbedtls_free(reexported);
}
/* END_CASE */

/* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_STORAGE_C */
void create_existent(int lifetime_arg, int owner_id_arg, int id_arg,
                     int reopen_policy_arg)
{
    psa_key_lifetime_t lifetime = lifetime_arg;
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(owner_id_arg, id_arg);
    mbedtls_svc_key_id_t returned_id = MBEDTLS_SVC_KEY_ID_INIT;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    psa_key_type_t type1 = PSA_KEY_TYPE_RAW_DATA;
    const uint8_t material1[5] = "a key";
    const uint8_t material2[5] = "b key";
    size_t bits1 = PSA_BYTES_TO_BITS(sizeof(material1));
    uint8_t reexported[sizeof(material1)];
    size_t reexported_length;
    reopen_policy_t reopen_policy = reopen_policy_arg;

    TEST_USES_KEY_ID(id);

    PSA_ASSERT(psa_crypto_init());

    /* Create a key. */
    psa_set_key_id(&attributes, id);
    psa_set_key_lifetime(&attributes, lifetime);
    psa_set_key_type(&attributes, type1);
    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT);
    psa_set_key_algorithm(&attributes, 0);
    PSA_ASSERT(psa_import_key(&attributes, material1, sizeof(material1),
                              &returned_id));
    TEST_ASSERT(mbedtls_svc_key_id_equal(id, returned_id));

    if (reopen_policy == CLOSE_BEFORE) {
        PSA_ASSERT(psa_close_key(id));
    }

    /* Attempt to create a new key in the same slot. */
    TEST_EQUAL(psa_import_key(&attributes, material2, sizeof(material2),
                              &returned_id),
               PSA_ERROR_ALREADY_EXISTS);
    TEST_ASSERT(mbedtls_svc_key_id_is_null(returned_id));

    if (reopen_policy == CLOSE_AFTER) {
        PSA_ASSERT(psa_close_key(id));
    }

    /* Check that the original key hasn't changed. */
    psa_reset_key_attributes(&attributes);
    PSA_ASSERT(psa_get_key_attributes(id, &attributes));
    TEST_ASSERT(mbedtls_svc_key_id_equal(
                    psa_get_key_id(&attributes), id));
    TEST_EQUAL(psa_get_key_lifetime(&attributes), lifetime);
    TEST_EQUAL(psa_get_key_type(&attributes), type1);
    TEST_EQUAL(psa_get_key_bits(&attributes), bits1);
    TEST_EQUAL(psa_get_key_usage_flags(&attributes), PSA_KEY_USAGE_EXPORT);
    TEST_EQUAL(psa_get_key_algorithm(&attributes), 0);

    PSA_ASSERT(psa_export_key(id,
                              reexported, sizeof(reexported),
                              &reexported_length));
    ASSERT_COMPARE(material1, sizeof(material1),
                   reexported, reexported_length);

    PSA_ASSERT(psa_close_key(id));

exit:
    /*
     * Key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&attributes);

    PSA_DONE();
}
/* END_CASE */

/* BEGIN_CASE */
void open_fail(int id_arg,
               int expected_status_arg)
{
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(1, id_arg);
    psa_status_t expected_status = expected_status_arg;
    psa_key_handle_t handle = mbedtls_svc_key_id_make(0xdead, 0xdead);

    PSA_ASSERT(psa_crypto_init());

    TEST_EQUAL(psa_open_key(id, &handle), expected_status);
    TEST_ASSERT(psa_key_handle_is_null(handle));

exit:
    PSA_DONE();
}
/* END_CASE */

/* BEGIN_CASE */
void create_fail(int lifetime_arg, int id_arg,
                 int expected_status_arg)
{
    psa_key_lifetime_t lifetime = lifetime_arg;
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(1, id_arg);
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    psa_status_t expected_status = expected_status_arg;
    mbedtls_svc_key_id_t returned_id =
        mbedtls_svc_key_id_make(0xdead, 0xdead);
    uint8_t material[1] = { 'k' };

    TEST_USES_KEY_ID(id);

    PSA_ASSERT(psa_crypto_init());

    psa_set_key_lifetime(&attributes, lifetime);
    if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
        /*
         * Not possible to set a key identifier different from 0 through
         * PSA key attributes APIs thus accessing to the attributes
         * directly.
         */
        attributes.core.id = id;
    } else {
        psa_set_key_id(&attributes, id);
    }

    psa_set_key_type(&attributes, PSA_KEY_TYPE_RAW_DATA);
    TEST_EQUAL(psa_import_key(&attributes, material, sizeof(material),
                              &returned_id),
               expected_status);
    TEST_ASSERT(mbedtls_svc_key_id_is_null(returned_id));

exit:
    PSA_DONE();
}
/* END_CASE */

/* BEGIN_CASE */
void copy_across_lifetimes(int source_lifetime_arg, int source_owner_id_arg,
                           int source_id_arg, int source_usage_arg,
                           int source_alg_arg, int source_alg2_arg,
                           int type_arg, data_t *material,
                           int target_lifetime_arg, int target_owner_id_arg,
                           int target_id_arg, int target_usage_arg,
                           int target_alg_arg, int target_alg2_arg,
                           int expected_usage_arg,
                           int expected_alg_arg, int expected_alg2_arg)
{
    psa_key_lifetime_t source_lifetime = source_lifetime_arg;
    mbedtls_svc_key_id_t source_id =
        mbedtls_svc_key_id_make(source_owner_id_arg, source_id_arg);
    psa_key_usage_t source_usage = source_usage_arg;
    psa_algorithm_t source_alg = source_alg_arg;
    psa_key_attributes_t source_attributes = PSA_KEY_ATTRIBUTES_INIT;
    psa_key_type_t source_type = type_arg;
    mbedtls_svc_key_id_t returned_source_id = MBEDTLS_SVC_KEY_ID_INIT;
    psa_key_lifetime_t target_lifetime = target_lifetime_arg;
    mbedtls_svc_key_id_t target_id =
        mbedtls_svc_key_id_make(target_owner_id_arg, target_id_arg);
    psa_key_usage_t target_usage = target_usage_arg;
    psa_algorithm_t target_alg = target_alg_arg;
    psa_key_attributes_t target_attributes = PSA_KEY_ATTRIBUTES_INIT;
    mbedtls_svc_key_id_t returned_target_id = MBEDTLS_SVC_KEY_ID_INIT;
    psa_key_handle_t target_handle = PSA_KEY_HANDLE_INIT;
    psa_key_usage_t expected_usage = expected_usage_arg;
    psa_algorithm_t expected_alg = expected_alg_arg;
    psa_algorithm_t expected_alg2 = expected_alg2_arg;
    uint8_t *export_buffer = NULL;

    TEST_USES_KEY_ID(source_id);
    TEST_USES_KEY_ID(target_id);

    PSA_ASSERT(psa_crypto_init());

    /* Populate the source slot. */
    psa_set_key_id(&source_attributes, source_id);
    psa_set_key_lifetime(&source_attributes, source_lifetime);

    psa_set_key_type(&source_attributes, source_type);
    psa_set_key_usage_flags(&source_attributes, source_usage);
    psa_set_key_algorithm(&source_attributes, source_alg);
    psa_set_key_enrollment_algorithm(&source_attributes, source_alg2_arg);
    PSA_ASSERT(psa_import_key(&source_attributes,
                              material->x, material->len,
                              &returned_source_id));
    /* Update the attributes with the bit size. */
    PSA_ASSERT(psa_get_key_attributes(returned_source_id,
                                      &source_attributes));

    /* Prepare the target slot. */
    psa_set_key_id(&target_attributes, target_id);
    psa_set_key_lifetime(&target_attributes, target_lifetime);

    psa_set_key_usage_flags(&target_attributes, target_usage);
    psa_set_key_algorithm(&target_attributes, target_alg);
    psa_set_key_enrollment_algorithm(&target_attributes, target_alg2_arg);

    /* Copy the key. */
    PSA_ASSERT(psa_copy_key(returned_source_id,
                            &target_attributes, &returned_target_id));

    /* Destroy the source to ensure that this doesn't affect the target. */
    PSA_ASSERT(psa_destroy_key(returned_source_id));

    /* If the target key is persistent, restart the system to make
     * sure that the material is still alive. */
    if (!PSA_KEY_LIFETIME_IS_VOLATILE(target_lifetime)) {
        mbedtls_psa_crypto_free();
        PSA_ASSERT(psa_crypto_init());
        PSA_ASSERT(psa_open_key(target_id, &target_handle));
    }

    /* Test that the target slot has the expected content. */
    psa_reset_key_attributes(&target_attributes);
    PSA_ASSERT(psa_get_key_attributes(returned_target_id,
                                      &target_attributes));

    if (!PSA_KEY_LIFETIME_IS_VOLATILE(target_lifetime)) {
        TEST_ASSERT(mbedtls_svc_key_id_equal(
                        target_id, psa_get_key_id(&target_attributes)));
    } else {
#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
        TEST_EQUAL(MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(returned_target_id),
                   target_owner_id_arg);
#endif
    }

    TEST_EQUAL(target_lifetime, psa_get_key_lifetime(&target_attributes));
    TEST_EQUAL(source_type, psa_get_key_type(&target_attributes));
    TEST_EQUAL(psa_get_key_bits(&source_attributes),
               psa_get_key_bits(&target_attributes));
    TEST_EQUAL(expected_usage, psa_get_key_usage_flags(&target_attributes));
    TEST_EQUAL(expected_alg, psa_get_key_algorithm(&target_attributes));
    TEST_EQUAL(expected_alg2,
               psa_get_key_enrollment_algorithm(&target_attributes));
    if (expected_usage & PSA_KEY_USAGE_EXPORT) {
        size_t length;
        ASSERT_ALLOC(export_buffer, material->len);
        PSA_ASSERT(psa_export_key(returned_target_id, export_buffer,
                                  material->len, &length));
        ASSERT_COMPARE(material->x, material->len,
                       export_buffer, length);
    } else {
        size_t length;
        /* Check that the key is actually non-exportable. */
        TEST_EQUAL(psa_export_key(returned_target_id, export_buffer,
                                  material->len, &length),
                   PSA_ERROR_NOT_PERMITTED);
    }

    PSA_ASSERT(psa_destroy_key(returned_target_id));

exit:
    /*
     * Source and target key attributes may have been returned by
     * psa_get_key_attributes() thus reset them as required.
     */
    psa_reset_key_attributes(&source_attributes);
    psa_reset_key_attributes(&target_attributes);

    PSA_DONE();
    mbedtls_free(export_buffer);
}
/* END_CASE */

/* BEGIN_CASE */
void copy_to_occupied(int source_lifetime_arg, int source_id_arg,
                      int source_usage_arg, int source_alg_arg,
                      int source_type_arg, data_t *source_material,
                      int target_lifetime_arg, int target_id_arg,
                      int target_usage_arg, int target_alg_arg,
                      int target_type_arg, data_t *target_material)
{
    psa_key_lifetime_t source_lifetime = source_lifetime_arg;
    mbedtls_svc_key_id_t source_id =
        mbedtls_svc_key_id_make(1, source_id_arg);
    psa_key_usage_t source_usage = source_usage_arg;
    psa_algorithm_t source_alg = source_alg_arg;
    psa_key_type_t source_type = source_type_arg;
    mbedtls_svc_key_id_t returned_source_id = MBEDTLS_SVC_KEY_ID_INIT;
    psa_key_lifetime_t target_lifetime = target_lifetime_arg;
    mbedtls_svc_key_id_t target_id =
        mbedtls_svc_key_id_make(1, target_id_arg);
    psa_key_usage_t target_usage = target_usage_arg;
    psa_algorithm_t target_alg = target_alg_arg;
    psa_key_type_t target_type = target_type_arg;
    mbedtls_svc_key_id_t returned_target_id = MBEDTLS_SVC_KEY_ID_INIT;
    mbedtls_svc_key_id_t new_key = MBEDTLS_SVC_KEY_ID_INIT;
    uint8_t *export_buffer = NULL;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    psa_key_attributes_t attributes1 = PSA_KEY_ATTRIBUTES_INIT;
    psa_key_attributes_t attributes2 = PSA_KEY_ATTRIBUTES_INIT;

    TEST_USES_KEY_ID(source_id);
    TEST_USES_KEY_ID(target_id);

    PSA_ASSERT(psa_crypto_init());

    /* Populate the source slot. */
    if (!PSA_KEY_LIFETIME_IS_VOLATILE(source_lifetime)) {
        psa_set_key_id(&attributes, source_id);
        psa_set_key_lifetime(&attributes, source_lifetime);
    }
    psa_set_key_type(&attributes, source_type);
    psa_set_key_usage_flags(&attributes, source_usage);
    psa_set_key_algorithm(&attributes, source_alg);
    PSA_ASSERT(psa_import_key(&attributes,
                              source_material->x, source_material->len,
                              &returned_source_id));

    /* Populate the target slot. */
    if (mbedtls_svc_key_id_equal(target_id, source_id)) {
        returned_target_id = returned_source_id;
    } else {
        psa_set_key_id(&attributes1, target_id);
        psa_set_key_lifetime(&attributes1, target_lifetime);
        psa_set_key_type(&attributes1, target_type);
        psa_set_key_usage_flags(&attributes1, target_usage);
        psa_set_key_algorithm(&attributes1, target_alg);
        PSA_ASSERT(psa_import_key(&attributes1,
                                  target_material->x, target_material->len,
                                  &returned_target_id));
    }

    PSA_ASSERT(psa_get_key_attributes(returned_target_id, &attributes1));

    /* Make a copy attempt. */
    psa_set_key_id(&attributes, target_id);
    psa_set_key_lifetime(&attributes, target_lifetime);
    TEST_EQUAL(psa_copy_key(returned_source_id,
                            &attributes, &new_key),
               PSA_ERROR_ALREADY_EXISTS);
    TEST_ASSERT(mbedtls_svc_key_id_is_null(new_key));

    /* Test that the target slot is unaffected. */
    PSA_ASSERT(psa_get_key_attributes(returned_target_id, &attributes2));
    TEST_ASSERT(mbedtls_svc_key_id_equal(
                    psa_get_key_id(&attributes1),
                    psa_get_key_id(&attributes2)));
    TEST_EQUAL(psa_get_key_lifetime(&attributes1),
               psa_get_key_lifetime(&attributes2));
    TEST_EQUAL(psa_get_key_type(&attributes1),
               psa_get_key_type(&attributes2));
    TEST_EQUAL(psa_get_key_bits(&attributes1),
               psa_get_key_bits(&attributes2));
    TEST_EQUAL(psa_get_key_usage_flags(&attributes1),
               psa_get_key_usage_flags(&attributes2));
    TEST_EQUAL(psa_get_key_algorithm(&attributes1),
               psa_get_key_algorithm(&attributes2));
    if (target_usage & PSA_KEY_USAGE_EXPORT) {
        size_t length;
        ASSERT_ALLOC(export_buffer, target_material->len);
        PSA_ASSERT(psa_export_key(returned_target_id, export_buffer,
                                  target_material->len, &length));
        ASSERT_COMPARE(target_material->x, target_material->len,
                       export_buffer, length);
    }

    PSA_ASSERT(psa_destroy_key(returned_source_id));
    if (!mbedtls_svc_key_id_equal(target_id, source_id)) {
        PSA_ASSERT(psa_destroy_key(returned_target_id));
    }

exit:
    /*
     * Key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&attributes1);
    psa_reset_key_attributes(&attributes2);

    PSA_DONE();
    mbedtls_free(export_buffer);
}
/* END_CASE */

/* BEGIN_CASE */
void invalid_handle(int handle_construction,
                    int close_status_arg)
{
    psa_key_handle_t valid_handle = PSA_KEY_HANDLE_INIT;
    psa_key_handle_t invalid_handle = PSA_KEY_HANDLE_INIT;
    psa_key_id_t key_id;
    psa_status_t close_status = close_status_arg;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    uint8_t material[1] = "a";

    PSA_ASSERT(psa_crypto_init());

    /* Allocate a handle and store a key in it. */
    psa_set_key_type(&attributes, PSA_KEY_TYPE_RAW_DATA);
    psa_set_key_usage_flags(&attributes, 0);
    psa_set_key_algorithm(&attributes, 0);
    PSA_ASSERT(psa_import_key(&attributes,
                              material, sizeof(material),
                              &valid_handle));
    TEST_ASSERT(!psa_key_handle_is_null(valid_handle));

    /* Construct an invalid handle as specified in the test case data. */
    switch (handle_construction) {
        case INVALID_HANDLE_0:
            invalid_handle = PSA_KEY_HANDLE_INIT;
            break;
        case INVALID_HANDLE_UNOPENED:

            /*
             * MBEDTLS_SVC_KEY_ID_GET_KEY_ID( valid_handle ) is a volatile
             * key identifier as the imported key is a volatile key. Volatile
             * key identifiers are in the range from PSA_KEY_ID_VOLATILE_MIN
             * to PSA_KEY_ID_VOLATILE_MAX included. Thus pick a key identifier
             * in the range from PSA_KEY_ID_VOLATILE_MIN to
             * PSA_KEY_ID_VOLATILE_MAX different from
             * MBEDTLS_SVC_KEY_ID_GET_KEY_ID( valid_handle ) to build an
             * unopened and thus invalid identifier.
             */

            if (MBEDTLS_SVC_KEY_ID_GET_KEY_ID(valid_handle) ==
                PSA_KEY_ID_VOLATILE_MIN) {
                key_id = PSA_KEY_ID_VOLATILE_MIN + 1;
            } else {
                key_id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(valid_handle) - 1;
            }

            invalid_handle =
                mbedtls_svc_key_id_make(0, key_id);
            break;
        case INVALID_HANDLE_CLOSED:
            PSA_ASSERT(psa_import_key(&attributes,
                                      material, sizeof(material),
                                      &invalid_handle));
            PSA_ASSERT(psa_destroy_key(invalid_handle));
            break;
        case INVALID_HANDLE_HUGE:
            invalid_handle =
                mbedtls_svc_key_id_make(0, PSA_KEY_ID_VENDOR_MAX + 1);
            break;
        default:
            TEST_ASSERT(!"unknown handle construction");
    }

    /* Attempt to use the invalid handle. */
    TEST_EQUAL(psa_get_key_attributes(invalid_handle, &attributes),
               PSA_ERROR_INVALID_HANDLE);
    TEST_EQUAL(psa_close_key(invalid_handle), close_status);
    TEST_EQUAL(psa_destroy_key(invalid_handle), close_status);

    /* After all this, check that the original handle is intact. */
    PSA_ASSERT(psa_get_key_attributes(valid_handle, &attributes));
    TEST_EQUAL(psa_get_key_type(&attributes), PSA_KEY_TYPE_RAW_DATA);
    TEST_EQUAL(psa_get_key_bits(&attributes),
               PSA_BYTES_TO_BITS(sizeof(material)));
    PSA_ASSERT(psa_close_key(valid_handle));

exit:
    /*
     * Key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&attributes);

    PSA_DONE();
}
/* END_CASE */

/* BEGIN_CASE */
void many_transient_keys(int max_keys_arg)
{
    mbedtls_svc_key_id_t *keys = NULL;
    size_t max_keys = max_keys_arg;
    size_t i, j;
    psa_status_t status;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    uint8_t exported[sizeof(size_t)];
    size_t exported_length;

    ASSERT_ALLOC(keys, max_keys);
    PSA_ASSERT(psa_crypto_init());

    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT);
    psa_set_key_algorithm(&attributes, 0);
    psa_set_key_type(&attributes, PSA_KEY_TYPE_RAW_DATA);

    for (i = 0; i < max_keys; i++) {
        status = psa_import_key(&attributes,
                                (uint8_t *) &i, sizeof(i),
                                &keys[i]);
        if (status == PSA_ERROR_INSUFFICIENT_MEMORY) {
            break;
        }
        PSA_ASSERT(status);
        TEST_ASSERT(!mbedtls_svc_key_id_is_null(keys[i]));
        for (j = 0; j < i; j++) {
            TEST_ASSERT(!mbedtls_svc_key_id_equal(keys[i], keys[j]));
        }
    }
    max_keys = i;

    for (i = 1; i < max_keys; i++) {
        PSA_ASSERT(psa_close_key(keys[i - 1]));
        PSA_ASSERT(psa_export_key(keys[i],
                                  exported, sizeof(exported),
                                  &exported_length));
        ASSERT_COMPARE(exported, exported_length,
                       (uint8_t *) &i, sizeof(i));
    }
    PSA_ASSERT(psa_close_key(keys[i - 1]));

exit:
    PSA_DONE();
    mbedtls_free(keys);
}
/* END_CASE */

/* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_STORAGE_C */
void key_slot_eviction_to_import_new_key(int lifetime_arg)
{
    psa_key_lifetime_t lifetime = (psa_key_lifetime_t) lifetime_arg;
    size_t i;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    uint8_t exported[sizeof(size_t)];
    size_t exported_length;
    mbedtls_svc_key_id_t key, returned_key_id;

    PSA_ASSERT(psa_crypto_init());

    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT);
    psa_set_key_algorithm(&attributes, 0);
    psa_set_key_type(&attributes, PSA_KEY_TYPE_RAW_DATA);

    /*
     * Create MBEDTLS_PSA_KEY_SLOT_COUNT persistent keys.
     */
    for (i = 0; i < MBEDTLS_PSA_KEY_SLOT_COUNT; i++) {
        key = mbedtls_svc_key_id_make(i, i + 1);
        psa_set_key_id(&attributes, key);
        PSA_ASSERT(psa_import_key(&attributes,
                                  (uint8_t *) &i, sizeof(i),
                                  &returned_key_id));
        TEST_ASSERT(mbedtls_svc_key_id_equal(returned_key_id, key));
    }

    /*
     * Create a new persistent or volatile key. When creating the key,
     * one of the descriptions of the previously created persistent keys
     * is removed from the RAM key slots. This makes room to store its
     * description in RAM.
     */
    i = MBEDTLS_PSA_KEY_SLOT_COUNT;
    key = mbedtls_svc_key_id_make(i, i + 1);
    psa_set_key_id(&attributes, key);
    psa_set_key_lifetime(&attributes, lifetime);

    PSA_ASSERT(psa_import_key(&attributes,
                              (uint8_t *) &i, sizeof(i),
                              &returned_key_id));
    if (lifetime != PSA_KEY_LIFETIME_VOLATILE) {
        TEST_ASSERT(mbedtls_svc_key_id_equal(returned_key_id, key));
    } else {
        TEST_ASSERT(psa_key_id_is_volatile(
                        MBEDTLS_SVC_KEY_ID_GET_KEY_ID(returned_key_id)));
    }

    /*
     * Check that we can export all ( MBEDTLS_PSA_KEY_SLOT_COUNT + 1 ) keys,
     * that they have the expected value and destroy them. In that process,
     * the description of the persistent key that was evicted from the RAM
     * slots when creating the last key is restored in a RAM slot to export
     * its value.
     */
    for (i = 0; i <= MBEDTLS_PSA_KEY_SLOT_COUNT; i++) {
        if (i < MBEDTLS_PSA_KEY_SLOT_COUNT) {
            key = mbedtls_svc_key_id_make(i, i + 1);
        } else {
            key = returned_key_id;
        }

        PSA_ASSERT(psa_export_key(key,
                                  exported, sizeof(exported),
                                  &exported_length));
        ASSERT_COMPARE(exported, exported_length,
                       (uint8_t *) &i, sizeof(i));
        PSA_ASSERT(psa_destroy_key(key));
    }

exit:
    PSA_DONE();
}
/* END_CASE */

/* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_STORAGE_C */
void non_reusable_key_slots_integrity_in_case_of_key_slot_starvation()
{
    psa_status_t status;
    size_t i;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    uint8_t exported[sizeof(size_t)];
    size_t exported_length;
    mbedtls_svc_key_id_t persistent_key = MBEDTLS_SVC_KEY_ID_INIT;
    mbedtls_svc_key_id_t persistent_key2 = MBEDTLS_SVC_KEY_ID_INIT;
    mbedtls_svc_key_id_t returned_key_id = MBEDTLS_SVC_KEY_ID_INIT;
    mbedtls_svc_key_id_t *keys = NULL;

    TEST_ASSERT(MBEDTLS_PSA_KEY_SLOT_COUNT >= 1);

    ASSERT_ALLOC(keys, MBEDTLS_PSA_KEY_SLOT_COUNT);
    PSA_ASSERT(psa_crypto_init());

    psa_set_key_usage_flags(&attributes,
                            PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_COPY);
    psa_set_key_algorithm(&attributes, 0);
    psa_set_key_type(&attributes, PSA_KEY_TYPE_RAW_DATA);

    /*
     * Create a persistent key
     */
    persistent_key = mbedtls_svc_key_id_make(0x100, 0x205);
    psa_set_key_id(&attributes, persistent_key);
    PSA_ASSERT(psa_import_key(&attributes,
                              (uint8_t *) &persistent_key,
                              sizeof(persistent_key),
                              &returned_key_id));
    TEST_ASSERT(mbedtls_svc_key_id_equal(returned_key_id, persistent_key));

    /*
     * Create MBEDTLS_PSA_KEY_SLOT_COUNT volatile keys
     */
    psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
    for (i = 0; i < MBEDTLS_PSA_KEY_SLOT_COUNT; i++) {
        PSA_ASSERT(psa_import_key(&attributes,
                                  (uint8_t *) &i, sizeof(i),
                                  &keys[i]));
    }
    psa_reset_key_attributes(&attributes);

    /*
     * Check that we cannot access the persistent key as all slots are
     * occupied by volatile keys and the implementation needs to load the
     * persistent key description in a slot to be able to access it.
     */
    status = psa_get_key_attributes(persistent_key, &attributes);
    TEST_EQUAL(status, PSA_ERROR_INSUFFICIENT_MEMORY);

    /*
     * Check we can export the volatile key created last and that it has the
     * expected value. Then, destroy it.
     */
    PSA_ASSERT(psa_export_key(keys[MBEDTLS_PSA_KEY_SLOT_COUNT - 1],
                              exported, sizeof(exported),
                              &exported_length));
    i = MBEDTLS_PSA_KEY_SLOT_COUNT - 1;
    ASSERT_COMPARE(exported, exported_length, (uint8_t *) &i, sizeof(i));
    PSA_ASSERT(psa_destroy_key(keys[MBEDTLS_PSA_KEY_SLOT_COUNT - 1]));

    /*
     * Check that we can now access the persistent key again.
     */
    PSA_ASSERT(psa_get_key_attributes(persistent_key, &attributes));
    TEST_ASSERT(mbedtls_svc_key_id_equal(attributes.core.id,
                                         persistent_key));

    /*
     * Check that we cannot copy the persistent key as all slots are occupied
     * by the persistent key and the volatile keys and the slot containing the
     * persistent key cannot be reclaimed as it contains the key to copy.
     */
    persistent_key2 = mbedtls_svc_key_id_make(0x100, 0x204);
    psa_set_key_id(&attributes, persistent_key2);
    status = psa_copy_key(persistent_key, &attributes, &returned_key_id);
    TEST_EQUAL(status, PSA_ERROR_INSUFFICIENT_MEMORY);

    /*
     * Check we can export the remaining volatile keys and that they have the
     * expected values.
     */
    for (i = 0; i < (MBEDTLS_PSA_KEY_SLOT_COUNT - 1); i++) {
        PSA_ASSERT(psa_export_key(keys[i],
                                  exported, sizeof(exported),
                                  &exported_length));
        ASSERT_COMPARE(exported, exported_length,
                       (uint8_t *) &i, sizeof(i));
        PSA_ASSERT(psa_destroy_key(keys[i]));
    }

    /*
     * Check we can export the persistent key and that it have the expected
     * value.
     */

    PSA_ASSERT(psa_export_key(persistent_key, exported, sizeof(exported),
                              &exported_length));
    ASSERT_COMPARE(exported, exported_length,
                   (uint8_t *) &persistent_key, sizeof(persistent_key));
exit:
    /*
     * Key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&attributes);

    psa_destroy_key(persistent_key);
    PSA_DONE();
    mbedtls_free(keys);
}
/* END_CASE */
